package ija.view.elements;

import ija.controller.types.GameState;
import ija.model.players.ComputerPlayer;
import ija.model.players.LocalPlayer;
import ija.model.players.Player;
import ija.model.players.RemotePlayer;
import ija.model.types.FigureColor;
import ija.model.types.FigureDirection;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.border.Border;

public class PlayerPanel extends JPanel implements ActionListener, KeyListener {
    public interface PlayerChangeListener {
        public void onPlayerChanged(PlayerPanel pp, Player p, int id);
    }

    private static final long serialVersionUID = 522053965574997533L;
    protected DisplayStatus status;
    protected PlayerType playerType = PlayerType.Local;
    protected GameState netStatus = GameState.GAMESETTINGS;
    protected Player player;
    protected int id;

    private JTextField tfName, tfIPAddress, tfPort;
    private JLabel lName, lIPAddress, lPort;
    private JLabel lNameValue, lIPAddressValue, lPortValue;
    private JComboBox<PlayerType> cbType;
    private JComboBox<FigureColor> cbColor;
    private JComboBox<FigureDirection> cbDirection;
    private JComboBox<Integer> cbDifficulty;
    private JLabel lColor, lDirection, lType, lDifficulty;
    private JLabel lColorValue, lDirectionValue, lTypeValue, lDifficultyValue;
    private JPanel pAI, pLocal, pNet;
    private JButton bHost, bJoin, bDisconnect;
    private JCheckBox chbConfirm;
    private GroupLayout layout, loNet, loAI;

    HashMap<String, JComponent> playerTypeComponents = new HashMap<String, JComponent>();
    List<PlayerChangeListener> listeners = new ArrayList<PlayerChangeListener>();

    public enum DisplayStatus {
        EDITABLE, NONEDITABLE, ;
    }

    public enum PlayerType {
        AI, Local, Net;
    }

    // region Getters
    public JComboBox<FigureColor> getColorCheckBox() {
        return this.cbColor;
    }

    public JComboBox<FigureDirection> getDirectionCheckBox() {
        return this.cbDirection;
    }

    // endregion Getters
    // region Setters
    public void setNetState(GameState netStatus) {
        if (this.playerType != PlayerType.Net) {
            netStatus = GameState.GAMESETTINGS;
        }

        switch (netStatus) {
        case CONNECTING:
        case GAMEINPROGRESS:
        case GAMEOVER:
        case HOSTING:
        case JOINING:
            this.setEditable(false);
            break;
        case GAMESETTINGS:
        default:
            this.setEditable(true);
            break;
        }

        this.chbConfirm.setVisible(false);
        this.bHost.setVisible(false);
        this.bJoin.setVisible(false);
        this.bDisconnect.setVisible(true);
        this.bDisconnect.setText("Disconnect");
        switch (netStatus) {
        case CONNECTING:
            this.bDisconnect.setText("Don't wait!");
            break;
        case HOSTING:
            this.chbConfirm.setVisible(true);
            break;
        case GAMEINPROGRESS:
            // this.bDisconnect.setVisible(false);
            break;
        case GAMESETTINGS:
            this.bHost.setVisible(true);
            this.bJoin.setVisible(true);
            // this.bDisconnect.setVisible(false);
            break;
        default:
            break;
        }
        this.netStatus = netStatus;
    }

    public void setFromPlayer(Player p) {
        this.player = p.duplicate();
        this.refresh();
    }

    public void setPlayerType(PlayerType pt) {
        if (this.playerType != pt) {
            this.cbType.setSelectedIndex(pt.ordinal());
        }
    }

    public void setDirectionType(FigureDirection fd) {
        if (this.player.getDirection().toString() != fd.toString()) {
            this.player.setDirection(fd);
            this.cbDirection.setSelectedIndex(fd.ordinal());
        }
    }

    public void setColorType(FigureColor fc) {
        if (this.player.getColor().toString() != fc.toString()) {
            this.player.setColor(fc);
            this.cbColor.setSelectedIndex(fc.ordinal());
        }
    }

    // endregion Setters

    // region Constructors
    public PlayerPanel(String name, FigureColor color, FigureDirection direction, DisplayStatus ds,
            int id) {
        super(new BorderLayout());
        Border blackline = BorderFactory.createLineBorder(Color.lightGray);
        this.setBorder(BorderFactory.createTitledBorder(blackline, name));
        this.player = new LocalPlayer(name, color, direction);
        this.playerType = PlayerType.Local;
        this.status = ds;
        this.addWidgets(name);
        this.id = id;
    }

    // endregion Constructors

    public void refresh() {
        String pt = this.playerType.toString();

        if (this.player instanceof LocalPlayer) {
            this.playerType = PlayerType.Local;
        } else if (this.player instanceof RemotePlayer) {
            this.playerType = PlayerType.Net;
        } else if (this.player instanceof ComputerPlayer) {
            this.playerType = PlayerType.AI;
        }

        if (pt != this.playerType.toString()) {
            this.layout.replace(this.playerTypeComponents.get(pt),
                    this.playerTypeComponents.get(this.playerType.toString()));
        }

        this.cbType.setSelectedItem(this.playerType);
        this.tfName.setText(this.player.getName());
        this.cbColor.setSelectedItem(this.player.getColor());
        this.cbDirection.setSelectedItem(this.player.getDirection());
        switch (this.playerType) {
        case AI:
            this.cbDifficulty.setSelectedItem(String.valueOf(((ComputerPlayer) this.player)
                    .getLevel()));
            break;
        case Net:
            this.tfIPAddress.setText(((RemotePlayer) this.player).getIp());
            this.tfPort.setText(String.valueOf(((RemotePlayer) this.player).getPort()));
            break;
        case Local:
            break;
        default:
            break;
        }
    }

    // region Widgets
    private void addWidgets(String s) {
        // region JComponents
        this.lName = new JLabel("Name:", SwingConstants.LEFT);
        this.lIPAddress = new JLabel("IP Address:", SwingConstants.LEFT);
        this.lPort = new JLabel("Port:", SwingConstants.LEFT);

        this.tfName = new JTextField(10);
        this.tfIPAddress = new JTextField("localhost", 10);
        this.tfPort = new JTextField(5);
        this.tfName.addKeyListener(this);
        this.tfIPAddress.addKeyListener(this);
        this.tfPort.addKeyListener(this);

        this.bHost = new JButton("Host");
        this.bHost.setPreferredSize(new Dimension(50, 20));
        this.bHost.setMinimumSize(new Dimension(50, 20));
        this.bHost.setMaximumSize(new Dimension(50, 20));

        this.bJoin = new JButton("Join");
        this.bJoin.setPreferredSize(new Dimension(50, 20));
        this.bJoin.setMinimumSize(new Dimension(50, 20));
        this.bJoin.setMaximumSize(new Dimension(50, 20));

        this.bDisconnect = new JButton("Disconnect");
        this.bDisconnect.setPreferredSize(new Dimension(50, 20));
        this.bDisconnect.setMinimumSize(new Dimension(50, 20));
        this.bDisconnect.setMaximumSize(new Dimension(50, 20));

        this.lNameValue = new JLabel(s, SwingConstants.RIGHT);
        this.lIPAddressValue = new JLabel("127.0.0.1", SwingConstants.RIGHT);
        this.lPortValue = new JLabel("8888", SwingConstants.RIGHT);

        this.chbConfirm = new JCheckBox("Accept game settings");
        this.chbConfirm.setSelected(false);

        this.lColor = new JLabel("Color:", SwingConstants.LEFT);
        this.lDirection = new JLabel("Direction:", SwingConstants.LEFT);
        this.lType = new JLabel("Type:", SwingConstants.LEFT);
        this.lDifficulty = new JLabel("Difficulty:", SwingConstants.LEFT);

        FigureColor[] fColors = { FigureColor.BLACK, FigureColor.WHITE };
        this.cbColor = new JComboBox<FigureColor>(fColors);
        FigureDirection[] fDirection = { FigureDirection.BACKWARD, FigureDirection.FORWARD };
        this.cbDirection = new JComboBox<FigureDirection>(fDirection);

        this.cbType = new JComboBox<PlayerType>(PlayerType.values());

        Integer[] iDifficulties = { 0, 1, 2, 3 };
        this.cbDifficulty = new JComboBox<Integer>(iDifficulties);

        this.cbType.addActionListener(this);
        this.cbColor.addActionListener(this);
        this.cbDirection.addActionListener(this);
        this.cbDifficulty.addActionListener(this);

        this.lColorValue = new JLabel("Black", SwingConstants.LEFT);
        this.lDirectionValue = new JLabel("Upwards", SwingConstants.LEFT);
        this.lTypeValue = new JLabel("Local", SwingConstants.LEFT);
        this.lDifficultyValue = new JLabel("0", SwingConstants.LEFT);

        this.pLocal = new JPanel(new BorderLayout());
        this.pLocal.setPreferredSize(new Dimension(0, 0));
        this.pLocal.setMaximumSize(new Dimension(0, 0));
        this.pAI = new JPanel(new BorderLayout());
        // endregion JComponents

        // region AI Layout
        this.loAI = new GroupLayout(this.pAI);
        this.pAI.setLayout(this.loAI);
        this.loAI.setAutoCreateGaps(true);
        this.loAI.setAutoCreateContainerGaps(true);
        this.loAI.setHorizontalGroup(this.loAI
                .createSequentialGroup()
                .addComponent(this.lDifficulty, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE,
                        Short.MAX_VALUE)
                .addComponent(this.cbDifficulty, GroupLayout.DEFAULT_SIZE,
                        GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE));

        this.loAI.setVerticalGroup(this.loAI
                .createParallelGroup(GroupLayout.Alignment.LEADING, false)
                .addComponent(this.lDifficulty, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addComponent(this.cbDifficulty, GroupLayout.DEFAULT_SIZE,
                        GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE));
        // endregion AI Layout
        // region Net Layout
        this.pNet = new JPanel(new BorderLayout());
        this.loNet = new GroupLayout(this.pNet);
        this.pNet.setLayout(this.loNet);
        this.loNet.setAutoCreateGaps(true);
        this.loNet.setAutoCreateContainerGaps(true);
        this.loNet.setHorizontalGroup(this.loNet
                .createParallelGroup(GroupLayout.Alignment.LEADING, false)
                .addGroup(
                        this.loNet
                                .createSequentialGroup()
                                .addComponent(this.lIPAddress, GroupLayout.DEFAULT_SIZE,
                                        GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                .addComponent(this.tfIPAddress, GroupLayout.DEFAULT_SIZE,
                                        GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addGroup(
                        this.loNet
                                .createSequentialGroup()
                                .addComponent(this.lPort, 0, GroupLayout.PREFERRED_SIZE,
                                        Short.MAX_VALUE)
                                .addComponent(this.tfPort, 0, GroupLayout.PREFERRED_SIZE,
                                        Short.MAX_VALUE))
                .addGroup(
                        this.loNet
                                .createSequentialGroup()
                                .addComponent(this.bHost, 0, GroupLayout.PREFERRED_SIZE,
                                        Short.MAX_VALUE)
                                .addComponent(this.bJoin, 0, GroupLayout.PREFERRED_SIZE,
                                        Short.MAX_VALUE))
                .addComponent(this.bDisconnect, 0, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE)
                .addComponent(this.chbConfirm, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE,
                        Short.MAX_VALUE));

        this.loNet.setVerticalGroup(this.loNet
                .createSequentialGroup()
                .addGroup(
                        this.loNet
                                .createParallelGroup(GroupLayout.Alignment.LEADING, false)
                                .addComponent(this.lIPAddress, GroupLayout.DEFAULT_SIZE,
                                        GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                .addComponent(this.tfIPAddress, GroupLayout.DEFAULT_SIZE,
                                        GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addGroup(
                        this.loNet
                                .createParallelGroup(GroupLayout.Alignment.LEADING, false)
                                .addComponent(this.lPort, GroupLayout.DEFAULT_SIZE,
                                        GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                .addComponent(this.tfPort, GroupLayout.DEFAULT_SIZE,
                                        GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addGroup(
                        this.loNet
                                .createParallelGroup(GroupLayout.Alignment.LEADING, false)
                                .addComponent(this.bHost, GroupLayout.DEFAULT_SIZE,
                                        GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                .addComponent(this.bJoin, GroupLayout.DEFAULT_SIZE,
                                        GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addComponent(this.bDisconnect, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE,
                        Short.MAX_VALUE)
                .addComponent(this.chbConfirm, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE,
                        Short.MAX_VALUE));
        // endregion Net Layout

        this.playerTypeComponents.put(PlayerType.Local.toString(), this.pLocal);
        this.playerTypeComponents.put(PlayerType.Net.toString(), this.pNet);
        this.playerTypeComponents.put(PlayerType.AI.toString(), this.pAI);

        // region Layout
        this.layout = new GroupLayout(this);
        this.setLayout(this.layout);
        this.layout.setAutoCreateGaps(true);
        this.layout.setAutoCreateContainerGaps(true);
        this.layout
                .setHorizontalGroup(this.layout
                        .createParallelGroup(GroupLayout.Alignment.LEADING, true)
                        .addGroup(
                                this.layout
                                        .createSequentialGroup()
                                        .addGroup(
                                                this.layout
                                                        .createParallelGroup(
                                                                GroupLayout.Alignment.LEADING,
                                                                false)
                                                        .addComponent(this.lName,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                Short.MAX_VALUE)
                                                        .addComponent(this.lColor,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                Short.MAX_VALUE)
                                                        .addComponent(this.lDirection,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                Short.MAX_VALUE)
                                                        .addComponent(this.lType,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                Short.MAX_VALUE))
                                        .addGroup(
                                                this.layout
                                                        .createParallelGroup(
                                                                GroupLayout.Alignment.LEADING,
                                                                false)
                                                        .addComponent(this.tfName,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                Short.MAX_VALUE)
                                                        .addComponent(this.cbColor,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                Short.MAX_VALUE)
                                                        .addComponent(this.cbDirection,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                Short.MAX_VALUE)
                                                        .addComponent(this.cbType,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                Short.MAX_VALUE)))
                        .addComponent(this.playerTypeComponents.get(this.playerType.toString())));

        this.layout
                .setVerticalGroup(this.layout
                        .createSequentialGroup()

                        .addGroup(
                                this.layout
                                        .createParallelGroup(GroupLayout.Alignment.LEADING, false)
                                        .addGroup(
                                                this.layout
                                                        .createSequentialGroup()
                                                        .addComponent(this.lName,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                Short.MAX_VALUE)
                                                        .addComponent(this.lColor,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                Short.MAX_VALUE)
                                                        .addComponent(this.lDirection,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                Short.MAX_VALUE)
                                                        .addComponent(this.lType,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                Short.MAX_VALUE))
                                        .addGroup(
                                                this.layout
                                                        .createSequentialGroup()
                                                        .addComponent(this.tfName,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                Short.MAX_VALUE)
                                                        .addComponent(this.cbColor,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                Short.MAX_VALUE)
                                                        .addComponent(this.cbDirection,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                Short.MAX_VALUE)
                                                        .addComponent(this.cbType,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                Short.MAX_VALUE)))
                        .addComponent(this.playerTypeComponents.get(this.playerType.toString())));
        // endregion Layout
        if (this.status == DisplayStatus.NONEDITABLE) {
            this.status = DisplayStatus.EDITABLE;
            this.setNonEditable();
        }
    }

    public void setNonEditable() {
        if (this.status == DisplayStatus.EDITABLE) {
            this.lNameValue.setText(this.tfName.getText());
            this.lColorValue.setText(this.cbColor.getSelectedItem().toString());
            this.lDirectionValue.setText(this.cbDirection.getSelectedItem().toString());
            this.lTypeValue.setText(this.cbType.getSelectedItem().toString());
            this.lIPAddressValue.setText(this.tfIPAddress.getText());
            this.lPortValue.setText(this.tfPort.getText());
            this.lDifficultyValue.setText(this.cbDifficulty.getSelectedItem().toString());

            this.layout.replace(this.tfName, this.lNameValue);
            this.layout.replace(this.cbColor, this.lColorValue);
            this.layout.replace(this.cbDirection, this.lDirectionValue);
            this.layout.replace(this.cbType, this.lTypeValue);
            this.loNet.replace(this.tfIPAddress, this.lIPAddressValue);
            this.loNet.replace(this.tfPort, this.lPortValue);
            this.loAI.replace(this.cbDifficulty, this.lDifficultyValue);
            this.status = DisplayStatus.NONEDITABLE;
        }
    }

    public void setEditable(boolean b) {
        if (b) {
            if (this.status == DisplayStatus.NONEDITABLE) {
                this.layout.replace(this.lNameValue, this.tfName);
                this.layout.replace(this.lColorValue, this.cbColor);
                this.layout.replace(this.lDirectionValue, this.cbDirection);
                this.layout.replace(this.lTypeValue, this.cbType);
                this.loNet.replace(this.lIPAddressValue, this.tfIPAddress);
                this.loNet.replace(this.lPortValue, this.tfPort);
                this.loAI.replace(this.lDifficultyValue, this.cbDifficulty);
                this.status = DisplayStatus.EDITABLE;
            }
        } else {
            this.setNonEditable();
        }
    }

    // endregion Widgets

    // region Listeners

    public void addChangeListener(PlayerChangeListener toAdd) {
        this.listeners.add(toAdd);
    }

    public void addHostListener(ActionListener host) {
        this.bHost.addActionListener(host);
    }

    public void addJoinListener(ActionListener join) {
        this.bJoin.addActionListener(join);
    }

    public void addDisconnectListener(ActionListener disc) {
        this.bDisconnect.addActionListener(disc);
    }

    public void addConfirmListener(ActionListener al) {
        this.chbConfirm.addActionListener(al);
    }

    private void playerChanged(int id) {
        for (PlayerChangeListener pcl : this.listeners) {
            pcl.onPlayerChanged(this, this.player, id);
        }
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        boolean Changed = false;

        if (e.getSource() instanceof JComboBox) {
            @SuppressWarnings("rawtypes")
            JComboBox cb = (JComboBox) e.getSource();
            // String item = cb.getSelectedItem().toString();

            if (cb == this.cbColor) {
                FigureColor fColor = (FigureColor) cb.getSelectedItem();
                if (this.player.getColor() != fColor) {
                    this.player.setColor(fColor);
                    Changed = true;
                }
            } else

            if (cb == this.cbDirection) {
                FigureDirection fDirection = (FigureDirection) cb.getSelectedItem();
                if (this.player.getDirection() != fDirection) {
                    this.player.setDirection(fDirection);
                    Changed = true;
                }
            } else

            if (cb == this.cbDifficulty) {
                Integer iDifficulty = (Integer) cb.getSelectedItem();
                if (((ComputerPlayer) this.player).getLevel() != iDifficulty) {
                    ((ComputerPlayer) this.player).setLevel(iDifficulty);
                    Changed = true;
                }
            } else

            if (cb == this.cbType) {
                PlayerType pType = (PlayerType) cb.getSelectedItem();
                if (this.playerType != pType) {
                    switch (pType) {
                    case AI:
                        ComputerPlayer cp = new ComputerPlayer();
                        cp.copyFrom(this.player);
                        cp.setLevel((Integer) this.cbDifficulty.getSelectedItem());
                        this.player = cp;
                        break;

                    case Local:
                        LocalPlayer lp = new LocalPlayer();
                        lp.copyFrom(this.player);
                        this.player = lp;
                        break;

                    case Net:
                        RemotePlayer np = new RemotePlayer();
                        np.copyFrom(this.player);
                        np.setIp(this.tfIPAddress.getText());
                        try {
                            np.setPort(Integer.parseInt(this.tfPort.getText()));
                        } catch (NumberFormatException exc) {
                            np.setPort(65535);
                            this.tfPort.setText("65535");
                        }
                        this.player = np;
                        break;
                    }
                    Changed = true;
                }
                this.refresh();
            }
        }
        if (Changed) {
            this.playerChanged(this.id);
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {
        String text;
        if (e.getSource() instanceof JTextField) {
            JTextField tf = (JTextField) e.getSource();
            text = tf.getText();
            if (tf == this.tfName) {
                this.player.setName(text);

            } else if (tf == this.tfPort) {
                ((RemotePlayer) this.player).setPort(Integer.parseInt(text));
            } else if (tf == this.tfIPAddress) {
                ((RemotePlayer) this.player).setIp(text);
            }
            this.playerChanged(this.id);
        }
    }

    // endregion Listeners

    // region Unused
    @Override
    public void keyTyped(KeyEvent e) {

    }

    @Override
    public void keyPressed(KeyEvent arg0) {
    }
    // endregion Unused
}